package org.ccay.core.orm.plugins;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.ccay.core.orm.PreparedStatementParams;

/**
 * 拦截处理类
 * 此类使用MyBatis的插件扩展机制， 只使用MyBatis的动态SQL功能，不查库
 */

/**
 * 拦截处理类
 * 此类使用MyBatis的插件扩展机制， 只使用MyBatis的动态SQL功能，不查库
 * @author chaowangbang
 * @since 2017年3月4日
 */
@Intercepts( { @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
	RowBounds.class, ResultHandler.class }) })
public class DynamicSqlInterceptor implements Interceptor {

	private static final int INDEX_MAPPED_STATEMENT = 0;

	private static final int INDEX_PARAMETER = 1;

	/**
	 * 拦截方法
	 * 
	 * @param invocation
	 *            拦截目标对象
	 * @return 拦截后的结果
	 * @throws Throwable
	 */
	public Object intercept(Invocation invocation) throws Throwable {
		Object[] queryArgs = invocation.getArgs();
		MappedStatement mappedStatement = (MappedStatement) queryArgs[INDEX_MAPPED_STATEMENT];
		//检查是否  是返回resultset
		if(checkResultSet( mappedStatement)){
			 Object preparedStatementParams = getSql(invocation,queryArgs);
			  return preparedStatementParams;
		}
		return invocation.proceed();
	}

	/**
	 * 获取动态sql结果
	 * @param invocation
	 * @param queryArgs
	 * @return
	 * @throws SQLException
	 */
	private Object getSql(Invocation invocation,Object[] queryArgs)throws SQLException {
		// 定义需要的变量
		final Object parameter = queryArgs[INDEX_PARAMETER];
		MappedStatement mappedStatement = (MappedStatement) queryArgs[INDEX_MAPPED_STATEMENT];
		BoundSql boundSql = mappedStatement.getBoundSql(parameter);
	
		//返回结果的容器
	    List<PreparedStatementParams> returnList = new ArrayList<PreparedStatementParams>();
	    
	    //结果对象
	    PreparedStatementParams preparedStatementParams = new PreparedStatementParams();
	    //获取 sql和 参数value 
		preparedStatementParams.setSql(boundSql.getSql());
		preparedStatementParams.setParameter(getParams(mappedStatement,boundSql));
		returnList.add(preparedStatementParams);
		//日志
		if(mappedStatement.getStatementLog().isDebugEnabled()){
			debug(mappedStatement.getStatementLog(),preparedStatementParams);
		}
		return returnList;
	}

	/**
	 * 记录日志
	 * @param preparedStatementParams
	 */
	private void debug(Log log,PreparedStatementParams preparedStatementParams) {
		StringBuffer logString = new StringBuffer("\nMyBatis Dynamic Sql Result :\n[SQL]");
		logString.append(preparedStatementParams.getSql());
		logString.append("\n[params]");
		for(int i=0;i<preparedStatementParams.getParameter().size();i++){
			if(i!=0){
				logString.append(",");
			}
			logString.append(preparedStatementParams.getParameter().get(i).toString());
		}
		log.debug(logString.toString());
	}

	private List<Object> getParams(MappedStatement mappedStatement, BoundSql boundSql) {
		ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
		
		//params we need
		Object parameterObject = boundSql.getParameterObject();
		Configuration configuration = mappedStatement.getConfiguration();
		TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		
		//result empty continer
		List<Object> params = new ArrayList<Object>();
		
		//parameterize
	    if (parameterMappings != null) {
	      for (int i = 0; i < parameterMappings.size(); i++) {
	        ParameterMapping parameterMapping = parameterMappings.get(i);
	        if (parameterMapping.getMode() != ParameterMode.OUT) {
	          Object value;
	          String propertyName = parameterMapping.getProperty();
	          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
	            value = boundSql.getAdditionalParameter(propertyName);
	          } else if (parameterObject == null) {
	            value = null;
	          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
	            value = parameterObject;
	          } else {
	            MetaObject metaObject = configuration.newMetaObject(parameterObject);
	            value = metaObject.getValue(propertyName);
	          }
	          params.add(value);
	        }
	      }
	    }
	    
	    return params;
	}
	/**
	 * dao  xml文件配置返回了类型是否是 PreparedStatementParams
	 * @param mappedStatement
	 * @return
	 */
	private boolean checkResultSet(MappedStatement mappedStatement){
		//如果不存在返回类型 那么直接返回false
		if(null == mappedStatement.getResultMaps() 
				|| mappedStatement.getResultMaps().isEmpty() 
				|| mappedStatement.getResultMaps().get(0) == null){
			return false;
		}
		Class<?> resultType = mappedStatement.getResultMaps().get(0).getType();
		//如果类型的PreparedStatementParams相同 那么返回true
		if(PreparedStatementParams.class.getName().equals(resultType.getName())){
			return true;
		}
		return false;
	}
	/**
	 * 插件方法
	 * 
	 * @param target
	 */
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	/* (non-Javadoc)
	 * @see org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
	 */
	public void setProperties(Properties arg0) {
	}
	
}
